﻿using System;
using System.Collections.Generic;
using System.Linq;
using Xenta.Api;
using Xenta.Entities;
using Xenta.Enums;
using Xenta.Security;
using Xenta.Structs;
using Xenta.Utils;

namespace Xenta.Operations
{
    #region __General__

    /// <summary>
    /// Creates a order item.
    /// </summary>
    public class CreateOrderItem : CreateAuditable<OrderItemEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
            }
        }

        /// <summary>
        /// Gets or sets the order identifier.
        /// </summary>
        public int OrderID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the SKU.
        /// </summary>
        public string SKU
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the price.
        /// </summary>
        public decimal Price
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the quantity.
        /// </summary>
        public decimal Quantity
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the tax amount.
        /// </summary>
        public decimal TaxAmount
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the discount amount.
        /// </summary>
        public decimal DiscountAmount
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the subtotal.
        /// </summary>
        public decimal Subtotal
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the total.
        /// </summary>
        public decimal Total
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public OrderItemFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            if(String.IsNullOrWhiteSpace(SKU))
                return Err.Of<OrderItemEntity>().NotValid("SKU");
            if(Price < Decimal.Zero)
                return Err.Of<OrderItemEntity>().NotValid("Price");
            if(Quantity <= Decimal.Zero)
                return Err.Of<OrderItemEntity>().NotValid("Quantity");
            if(TaxAmount < Decimal.Zero)
                return Err.Of<OrderItemEntity>().NotValid("TaxAmount");
            if(DiscountAmount < Decimal.Zero)
                return Err.Of<OrderItemEntity>().NotValid("DiscountAmount");
            if(Subtotal < Decimal.Zero)
                return Err.Of<OrderItemEntity>().NotValid("Subtotal");
            if(Total < Decimal.Zero)
                return Err.Of<OrderItemEntity>().NotValid("Total");
            return base.Validate(uow);
        }

        protected override void Construct(IUnitOfWork uow, OrderItemEntity entity)
        {
            base.Construct(uow, entity);

            entity.Order = uow.Execute(new GetOrder
            {
                ThrowsException = true,
                EntityID = OrderID
            }).Result;
            entity.SKU = SKU.TrimToLen(100);
            entity.Price = Price;
            entity.Quantity = Quantity;
            entity.TaxAmount = TaxAmount;
            entity.DiscountAmount = DiscountAmount;
            entity.Subtotal = Subtotal;
            entity.Total = Total;
            entity.Flags = Flags;
        }

        #endregion
    }

    /// <summary>
    /// Updates the order item.
    /// </summary>
    public class UpdateOrderItem : UpdateAuditable<OrderItemEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
            }
        }

        /// <summary>
        /// Gets or sets the SKU.
        /// </summary>
        public string SKU
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the price.
        /// </summary>
        public decimal Price
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the quantity.
        /// </summary>
        public decimal Quantity
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the tax amount.
        /// </summary>
        public decimal TaxAmount
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the discount amount.
        /// </summary>
        public decimal DiscountAmount
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the subtotal.
        /// </summary>
        public decimal Subtotal
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the total.
        /// </summary>
        public decimal Total
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public OrderItemFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            if(String.IsNullOrWhiteSpace(SKU))
                return Err.Of<OrderItemEntity>().NotValid("SKU");
            if(Price < Decimal.Zero)
                return Err.Of<OrderItemEntity>().NotValid("Price");
            if(Quantity <= Decimal.Zero)
                return Err.Of<OrderItemEntity>().NotValid("Quantity");
            if(TaxAmount < Decimal.Zero)
                return Err.Of<OrderItemEntity>().NotValid("TaxAmount");
            if(DiscountAmount < Decimal.Zero)
                return Err.Of<OrderItemEntity>().NotValid("DiscountAmount");
            if(Subtotal < Decimal.Zero)
                return Err.Of<OrderItemEntity>().NotValid("Subtotal");
            if(Total < Decimal.Zero)
                return Err.Of<OrderItemEntity>().NotValid("Total");
            return base.Validate(uow);
        }

        protected override void Revise(IUnitOfWork uow, OrderItemEntity entity)
        {
            base.Revise(uow, entity);

            entity.SKU = SKU.TrimToLen(100);
            entity.Price = Price;
            entity.Quantity = Quantity;
            entity.TaxAmount = TaxAmount;
            entity.DiscountAmount = DiscountAmount;
            entity.Subtotal = Subtotal;
            entity.Total = Total;
            entity.Flags = Flags;
        }

        #endregion
    }

    /// <summary>
    /// Deletes the order item.
    /// </summary>
    public class DeleteOrderItem : DeleteEntity<OrderItemEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
            }
        }

        #endregion

        #region Utilities

        protected override void Destruct(IUnitOfWork uow, OrderItemEntity entity)
        {
            foreach(var i in entity.Attributes)
            {
                uow.Execute(new DeleteOrderAttribute
                {
                    EntityID = i.EntityID
                });
            }
            base.Destruct(uow, entity);
        }

        #endregion
    }

    /// <summary>
    /// Gets the order item by identifier.
    /// </summary>
    public class GetOrderItem : GetEntity<OrderItemEntity>,
        IApiOperation
    {
    }

    #endregion

    #region __Search__

    /// <summary>
    /// Search over order item collection.
    /// </summary>
    public class SearchOrderItems : SearchOperation<OrderItemEntity>,
        IApiOperation
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public SearchOrderItems()
        {
            Term = SearchTerm.Empty;
            SortDir = SortDirection.Ascending;
            SortBy = new []
            {
                OrderItemSortBy.ID
            };
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the search term.
        /// </summary>
        public SearchTerm Term
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the order identifier.
        /// </summary>
        public int? OrderID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flag mask.
        /// </summary>
        public OrderItemFlags FlagMask
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the sort direction.
        /// </summary>
        public SortDirection SortDir
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the sort by.
        /// </summary>
        public OrderItemSortBy[] SortBy
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<OrderItemEntity> Build(IUnitOfWork uow, IQueryable<OrderItemEntity> query)
        {
            query = base.Build(uow, query);
            query = query.FilterByFlags(x => (int)x.Flags, (int)FlagMask);
            switch(Term.Mode)
            {
                case SearchTerm.SearchMode.StartsWith:
                    query = query.Where(x => x.SKU.StartsWith(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.EndsWith:
                    query = query.Where(x => x.SKU.EndsWith(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.Contains:
                    query = query.Where(x => x.SKU.Contains(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.Equals:
                    query = query.Where(x => x.SKU.Equals(Term.Keyword));
                    break;
            }
            if(OrderID.HasValue)
                query = query.Where(x => x.Order.EntityID == OrderID.Value);
            if(SortBy != null)
            {
                for(int i = 0; i < SortBy.Length; i++)
                {
                    switch(SortBy[i])
                    {
                        case OrderItemSortBy.Quantity:
                            query = query.OrderBy(x => x.Quantity, SortDir, i != 0);
                            break;
                        case OrderItemSortBy.CreatedOn:
                            query = query.OrderBy(x => x.CreatedOn, SortDir, i != 0);
                            break;
                        default:
                            query = query.OrderBy(x => x.EntityID, SortDir, i != 0);
                            break;
                    }
                }
            }
            return query;
        }

        #endregion
    }

    #endregion
}
